package org.jadapter.tests;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import junit.framework.TestCase;

import org.jadapter.Query;
import org.jadapter.Transformer;
import org.jadapter.func.Filter;
import org.jadapter.tests.examples.Incrementer;

@SuppressWarnings("unchecked")
public class TestQuery extends TestCase {           
    
    protected Query<String> query;

    
    public static <T> void assertQueryEquals(Query<T> mengi, List<T> args){
        int i = 0;
        for(T s : mengi){
            T x = args.get(i++);
            if(!s.equals(x)){
                fail(s + " != " + x);
            }
        }
    }    

    
    public static <T> void assertQueryEquals(Query<T> mengi, T ... args){
        assertQueryEquals(mengi, Arrays.asList(args));
    }   
    
    public void testConstructor(){       
        query = new Query<String>("a", "b", "c");
        assertQueryEquals(query, "a", "b", "c");
    }
    
    public void testNEW(){
        Query<String> q = Query.NEW("foo", "bar");
        assertQueryEquals(q, "foo", "bar");  
           
        Query<List<String>> ql = Query.NEW(Arrays.asList("foo", "bar"));
        assertQueryEquals(ql, Arrays.asList("foo", "bar"));  
        assertTrue(ql.iterator().next().iterator().next().equals("foo"));           
    }
      
    public void testShouldSliceAndCalculateLength(){
        query = new Query<String>("a", "b", "c");

        assertEquals(2, query.slice(1).length());
        assertEquals(1, query.slice(1).slice(1).length());      
        assertEquals(0, query.slice(1).slice(1).slice(1).length());
    }
    
    public void testShouldSliceAndGetElementsAndCalculateLength(){
        query = new Query<String>("a", "b", "c");

        assertEquals("a", query.get(0));
        assertEquals(2,   query.slice(1).length());
        assertEquals("b", query.slice(1).get(0));
        assertEquals(1,   query.slice(1).slice(1).length());
        assertEquals("c", query.slice(1).slice(1).get(0));
        assertEquals(0,   query.slice(1).slice(1).slice(1).length());
    }    
      
    

    
    public void testSlice(){
        query = new Query<String>("a", "b", "c");

    	assertQueryEquals(query.slice(0),   "a", "b", "c");
    	assertQueryEquals(query.slice(0,1), "a", "b");
    	assertQueryEquals(query.slice(0,2), "a", "b", "c");
    	assertQueryEquals(query.slice(1),   "b", "c");  
    	assertQueryEquals(query.slice(1,1), "b");
    	assertQueryEquals(query.slice(1,2), "b", "c");
    	assertQueryEquals(query.slice(2),   "c");
    	assertQueryEquals(query.slice(2,2), "c");
    }
    
    public void testFilter(){
        query = new Query<String>("a", "b", "c");

        query = query.filter(new Filter<String>(){
            public boolean filter(String a) {
                return a.equals("b");
            }          
        });
        
        assertQueryEquals(query, "b");
    }
    
    public void testShouldWorkInIteratorLoop(){
        query = new Query<String>("a", "b", "c");

        Iterator<String> it = query.iterator();
        int count = 0;
        while(it.hasNext()){
            it.next();
            count++;
        }
        assertEquals(3, count);
    }
    
    public void testShouldWorkUsingIteratorNext(){
        query = new Query<String>("a", "b", "c");

        Iterator<String> it = query.iterator();
        it.next();it.next();
        String s = it.next();
        assertEquals("c", s);
    }
    
    public void testSecondIteration() {
        query = new Query<String>("a", "b", "c");

        testShouldWorkUsingIteratorNext();
        testShouldWorkUsingIteratorNext();
    }
    
    public void testChaining(){
        
        query = new Query<String>("a", "au", "e", "ey")
            .map(new Transformer<String,String>(){
                public String transform(String t) {
                    return t.toUpperCase(); // capitalize each item
                }         
            })
            .filter(new Filter<String>(){
                public boolean filter(String a) {
                    return a.length() > 1; // remove single letter items
                }            
            })
            .slice(1); // skip the first item
        
        assertQueryEquals(query, "EY"); 
    }
    
    
    public void testLength(){
        query = new Query<String>("a", "b", "c");

        assertEquals(3, query.length());
    }
    
    public void testShouldChainSlices(){
        query = new Query<String>("a", "b", "c");

    	assertQueryEquals(query.slice(1).slice(1), "c");
    }
    
    public void testShouldAlsoWorkWithStrings() {
        query = new Query<String>("a", "b", "c");

        Query<String> query = new Query<String>("fo0"); 
        String foo = "";
        for (String c : query) {
            foo += c;
        }
        
        assertEquals("fo0", foo);
        assertQueryEquals(query.slice(1), "o", "0");
        
        assertEquals(3, query.length());
        assertEquals(2, query.slice(1).length());
        
        assertQueryEquals(query.slice(2),"0");   
        
        assertEquals("o", query.get(1));  
    }   
    
    public void testShouldMapStringToString() {
        Query<String> query = new Query<String>("fo0"); 
        
        Transformer<String,String> upperCase = new Transformer<String,String>(){
            public String transform(String a) {
                return a.toUpperCase();
            }  
        };
        
        assertQueryEquals(query.map(upperCase), "F", "O", "0");       
    }

    
    public void testMapQueryTyped() {
        Transformer<Integer,String> integers  = new Transformer<Integer,String>(){
            public Integer transform(String a) {
                return Integer.parseInt(a);
            }
        };
        
        assertQueryEquals(new Query("1", "2").map(integers), 1, 2);
              
    }

    
    public void testFilterQuery() {
        Query<String> query = new Query<String>("fo0"); 

        Filter<String> numbers = new Filter<String>(){
            public boolean filter(String a) {
                try {
                    Integer.parseInt(a);
                    return true;
                } catch (NumberFormatException e) {
                    return false;
                }          
            }
        };
        
        assertQueryEquals(query.filter(numbers), "0");       
    }

    
	class Counter implements Incrementer {
		int count = 0;
		public Counter inc() { count++; return this; }
	}    
    
    public void testShouldInvokeMethodOnAllElements(){
    	    	
    	Counter c0 = new Counter();
    	Counter c1 = new Counter().inc();
    	    	
    	Query<Incrementer> counters = new Query<Incrementer>(c0, c1);
    	counters.each().inc();
    	assertEquals(1, c0.count);
    	assertEquals(2, c1.count);
    	    	
    }
    
    public void testShouldAppendIterables(){
        query = new Query<String>("a", "b", "c");

        Query<String> q = query.append(new Query<String>("1", "2"));
        assertQueryEquals(q, "a", "b", "c", "1", "2");
    }
    
    public void testShouldPrependIterables(){
        query = new Query<String>("a", "b", "c");

        Query<String> q = query.prepend(new Query<String>("1", "2"));
        assertQueryEquals(q, "1", "2", "a", "b", "c");
    }    
    
             
}
